package a6;


import com.google.inject.Guice;
import com.google.inject.Injector;

/**
    Tests a scanner for the grammar with start symbol Program
    and productions
<pre>
       Program -> Expr | Local {Local} find Expr
       Local -> with Binding
       Binding -> VariableName Expr | UserFunctionName Definition
       Definition -> begin {VariableName} body Program end 
       Application -> FunctionName ( {Expr} )
       FunctionName -> UserFunctionName | + | -
       Expr -> IntegerLiteral | VariableName | Application
</pre>

    In these productions, the braces and the vertical bar
      are metasymbols, while the parentheses are terminal 
      symbols of the grammar.
    Also,
      <code>UserFunctionName</code> is a preterminal whose 
        corresponding terminal must start with a lower-case letter, 
        <code>VariableName</code> is a preterminal whose 
        corresponding terminal must start with a capital letter, 
        and <code>IntegerLiteral</code> is 
        a legal integer literal in Java
     @author Jeff Smith
     @version for Assignment 5, CS 152, SJSU, Fall 2012
  */


public class A6Injected
{

  
  /**
      Takes an input string purporting to represent a program in 
      L(G), where G is the context-free grammar defined above, and
      prints the sequence of tokens of the program, terminated by
      a dummy token.
      If an illegal token is present, it prints an error message 
      to that effect.
      @param expr the string, represented as a string of tokens 
        separated by whitespace characters as recognized by the 
        <code>Character.isWhitespace</code> predicate.
   */
      
  private static void test(String program){
	try {
		Injector injector = Guice.createInjector(new A6Module());
		Interpreter interpreter = injector.getInstance(Interpreter.class);
		System.out.println(interpreter.interpret(program));
	} catch (IllegalArgumentException e){
		System.out.println(e.getMessage());
	}
  }
  
  /**
      The main test program
      @param args is ignored
   */ 
  
  public static void main(String[] args)                                      {
    String newline = System.getProperty("line.separator");

    // syntactically and semantically ok
/* */
    test("-3"); // -3
    test("+ ( 3 4 ) "); // 7
    test("- ( 3 4 )"); // -1
    test(" + ( 3 - ( 4 5 ) )"); // 2
    test(" + ( - ( 3 4 ) 5 )"); // 4
    test("with X -4 find X"); // -4
    test("with X + ( 3 4 ) find X"); // 7
    test("with X 3 " + newline + "find + ( 1 X )"); // 4
    test("with X 3 with Y + ( 1 X ) find Y"); // 4
    test("with X 3 with Y + ( X 1 ) find X"); // 3
    test("with X 3 with Y + ( X 100 ) find + ( X - ( Y 1 ) )"); // 105
    test("with X 3 with f begin Y body + ( X Y  ) end find X"); // 3
    test("with X 3 with f begin Y Z body + ( X Z ) end find X"); // 3
    test("with X 3 with f begin Y body + ( X Y ) end find f ( 5 )"); // 8
    test("with f begin body 50 end find f ( )"); // 50
    test("with f begin X body + ( X X ) end " + // 222
         "find f ( 111 )"); 
    test("with f begin X body + ( X X ) end " + 
         "with F f ( 111 ) " +
         "find f ( F ) "); // 444
    test("with f begin X body + ( X X ) end " +
         "with g begin Y body - ( Y 5 ) end " +
         "find + ( f ( 10 ) g ( 8 ) )"); // 23
    test("with p begin X body + ( X X ) end " +
         "with q begin U V body - ( + ( U V ) 20 ) end " +
         "find + ( q ( 100 70 ) p ( 4 ) )"); // 158 
    test("with p begin X body + ( X X ) end " +
         "with q begin U V body - ( + ( U V ) 20 ) end " +
         "find p ( q ( 1000 70 ) )"); // 2100
    test("with p begin X body + ( X X ) end " +
         "with q begin U V body - ( + ( U V ) 20 ) end " +
         "find q ( p ( 100 ) p ( 30 ) )");  // 240
    test("with p begin X body + ( X X ) end " +
         "with q begin U V body - ( + ( U V ) 20 ) end " +
         "with r begin Z body q ( p ( Z ) 1 ) end " +
         "find r ( 2000 ) "); // 3981
    test("with g begin Y body - ( Y 5 ) end " +
         "with f begin X body " +
         "  + ( g ( X ) X ) end " +
         "find f ( 10 ) "); // 15
    test("with f begin X body " +
         "  with Y 8 " +
         "  find + ( X Y ) end " +
         "find f ( 10 ) "); // 18
    test("with f begin X body " +
         "  with g begin Y body - ( Y 1 ) end " +
         "  find + ( g ( X ) X ) end " +
         "find f ( 10 ) "); // 19
    test("with f begin X body - ( X 10 ) end " +
         "with f begin Y body + ( Y 5 ) end " +
         "find f ( 550 ) "); // 555
    test("with f begin X body + ( X 10 ) end " +
         "with g begin X body " +
         "  with W 2 find f ( + ( W X ) ) end " +
         "find g ( 4 ) "); // 16
    test("with f begin X body + ( g ( X ) 10 ) end " +
         "with g begin X body " +
         "  with f begin W body + ( W 20 ) end " +
         "  find + ( f ( 40 ) X ) end " +
         "find f ( 4 ) "); // 74
    test("with X 999 " +
         "with f begin XX body X end " +
         "find f ( 3 ) "); /// 999
    System.out.println();     
         
    // syntactically ok, semantically bad     
    test("X");    
    test("f ( 4 )"); 
    test("- ( 4 )"); 
    test("+ ( 4 5 6 )");  
    test("with X X find X");
    test("with Y + ( D 10 ) find + ( 5 - ( Y 1 ) )");      
    test("with X 2 find + ( X - ( E 1 ) )");      
    test("with Y + ( G 10 ) with G 2 find +  Y 1 )");      
    test("with f1 begin X body + ( Y 10 ) end " +
         "find f1 ( 3 ) ");
    test("with f begin X body + ( X 10 ) end " +
         "find f0 ( 3 ) ");
    test("with f2 begin X body " +
         "  with g2 begin Y body - ( Y 5 ) end " +
         "  find + ( g2 ( X ) X ) end " +
         "find g2 ( 10 ) ");
    test("with f3 begin X body " +
         "  with g3 begin X body h3 ( X 5 ) end " +
         "  find + ( g3 ( X ) X ) end " +
         "find f3 ( 10 ) ");
    test("with f4 begin X body + ( X 10 ) end " +
         "find f4 ( 3 4 ) ");
    test("with f5 begin X1 X2 X3 body + ( X1 X3 ) end " +
         "find f5 ( 3 4 ) ");
    test("with f6 begin X body + ( X 10 ) end " +
         "with f6 begin X Y body + ( Y 10 ) end " +
         "find f6 ( 4 ) ");
    test("with f9 begin X body + ( X X ) end " +
         "find + ( f9 ( 10 ) g9 ( 8 ) )");
    System.out.println();     
    
    // syntactically bad
    test(null);
    test("");
    test("-X");
    test("( 3 )");
    test("3X");    
    test("* ( 3 4 )");
    test("find + ( 3 4 )");
    test(" + ) 3 4 )");
    test(" -  3 4 )");
    test(" + ( 3 4 ");
    test(" + ( 3 + ) ");
    test(" 3 4 )");
    test(" + ( 3 - ( 4 5 ) ) )");
    test("with 3 X find X");
    test("with X1 find X1");
    test("with X2 2");
    test("with X2 3 with Y2 4");
    test("with X3 3 4 find X3");
    test("with X4 3 Y4 4 find ( + X4 Y4 )");
    test("with f1 4 " +
         "find f1 ( 10 )");
    test("with f2 begin X2 body end " +
         "find f2 ( 10 )");
    test("with f3 X3 begin end " +
         "find f3 ( 10 )");
    test("with f4 begin X4 end " +
         "find f4 ( 10 )");
    test("with F6 begin X body + ( X X ) end " +
         "find f6 ( 10 )");
    test("with f8 X body + ( X X ) end " +
         "find f8 ( 10 )");
    test("with begin f9 X body + ( X X ) end " +
         "find f9 ( 10 )");
    test("with f0 begin X + ( X X ) end " +
         "find f0 ( 10 )");
    test("with f2 begin X body ( X X ) end " +
         "find f2 ( 10 )");
    test("with f4 begin X body + ( X X ) end end " +
         "find f4 ( 10 )");
    test("with f5 begin X body + ( X f5 ) end " +
         "find f5 ( 10 )");
    test("with f6 begin X body + ( X X ) end " +
         "find f6 f6 ( 10 )");
    test("with f7 begin X body + ( X X ) end " +
         "find f7 ( f7 )");
    test("with f8 begin X body + ( X X ) end " +
         "find f8 ( 10 ");
    test("with p1 begin X body + ( X 10 ) end " +
         "find end ");    
    test("with p2 begin X body + ( X 10 ) end " +
         "find p2 ");    
    test("with p3 begin X body + ( X 10 ) end " +
         "find ( 3 4 ) ");    
    test("with p4 begin X body + ( X 10 ) end " +
         "find + ( 3 p4 ) ");    
    test("with p5 begin X body + ( X X ) end " +
         "with 3 begin U V body 3 ( U V ) end " +
         "find p5 ( 70 )");
    test("with p6 begin X body + ( X X ) end " +
         "with + begin U V body p6 ( U V ) end " +
         "find p6 ( 70 )");
    test("with p7 begin X body + ( X X ) end " +
         "with q7 begin U V body - ( + ( U V ) 20 ) end " +
         "find p7 ( q7 ( 100 70 ) ");
    test("with p8 begin X body " +
         "  with q8 begin X body - ( X 5 ) " +
         "  find + ( q8 ( X ) X ) end " +
         "find p8 ( 10 ) ");
    test("with p9 begin X body " +
         "  with q9 begin X body - ( X 5 ) end " +
         "  find + ( q9 ( X ) X ) " +
         "find p9 ( 10 ) ");
    test("with p0 begin X body " +
         "  with q0 begin X body - ( X 5 ) end " +
         "  find + ( q0 ( X ) X ) " +
         "find p0 ( 10 ) ");
    test("with f5 begin X body + ( X X ) - ( X 5 ) end " +  // ??
         "find f5 ( 10 )");
    test("with g0 begin f0 X body + ( X X ) end " +
         "find f0 ( 10 )");
    test("with f3 begin X body + ( X X ) " +
         "find f3 ( 10 )");
    System.out.println();     

    // compilation vs. interpretation
    test("with f begin X body + ( X 990 ) end " +
         "with g begin X body end " +
         "find f ( 3 ) ");
    test("with f begin X body + ( X 990 ) end " +
         "with g begin X body end " +
         "find g ( 3 ) ");
    System.out.println();     

    // reused variable names
    // recall the policy of overwriting variable names may give 
    //   some incorrect values, which is ok for this assignment
    // however you should be able to return *some* value
    
    test("with X 1 " +
         "with Y 2 " +
         "with X 3 " +
         "find + ( X Y )");
    test("with f begin X Y X body + ( X + ( Y X ) ) end " +
         "find f ( 1 2 3 ) ");
    test("with X 1 " +
         "with Y 2 " +
         "with f begin W body " +
         "  with X 3 " +
         "  find + ( X W ) end " +
         "find + ( f ( X ) X )");
    test("with X 1 " +
         "with f begin X body + ( X X ) end " +
         "find + ( f ( 10 ) X )");
    test("with f begin X body + ( X X ) end " +
         "with X 1 " +
         "find + ( f ( 10 ) X )");
    test("with f begin X body + ( X 10 ) end " +
         "with g begin X body + ( f ( + ( X 1 ) ) X ) end " +
         "find g ( 2 )");
    test("with X 1 " +
         "with f begin X body + ( X X ) end " +
         "find + ( f ( 10 ) X )");
    test("with f begin X body " +
         "  with g begin Y body " +
         "    with X 110 find - ( Y X ) end " +
         "  find + ( g ( X ) X ) end " +
         "find f ( 100 ) ");
    test("with f begin X body " +
         "  with g begin X body - ( X 5 ) end " +
         "  find + ( g ( + ( X 1 ) ) X ) end " +
         "find  f ( 50 )  ");
    test("with g begin Z body + ( Z 5 ) end " +
         "with f begin X body " +
         "  with g begin Y body - ( Y 5 ) end " +
         "  find + ( g ( + ( X 2 ) ) X ) end " +
         "find + (  f ( 10 ) g ( 10 ) ) ");
    System.out.println();     

    // scoping tests
    test("with f begin X Y body " +
         "  with h begin X W body + ( X - ( Y W ) ) end " +
         "  with g begin Y V body h ( 25 26 ) end " + 
         "  find + ( g ( 15 16 ) h ( 0 1 ) ) end " +
         "find f ( 5 6 ) ");
    test("with k begin XX body + ( XX 10 ) end " +
         "with h begin VV body k ( VV ) end " +
         "with g begin W body " + 
         "  with k begin X body + ( X 20 ) end " +
         "  find h ( W ) end " + 
         "find g ( 2 )");
    test("with k begin XX body + ( XX 10 ) end " +
         "with h begin VV body k ( VV ) end " +
         "with g begin W body " + 
         "  with k begin X body + ( X 20 ) end " +
         "  find h ( W ) end " + 
         "find h ( 2 )");
    test("with k begin XX body + ( XX 10 ) end " +
         "with h begin VV body k ( VV ) end " +
         "with g begin W body " + 
         "  with k begin X body + ( X 20 ) end " +
         "  find h ( W ) end " + 
         "find + ( g ( 2 ) h ( 2 ) )");


}

}